<?php

namespace System\Configure;

use System\Loader\PathAlias;
use System\Configure\Exception\FileNotReadException;
use System\Configure\Exception\FileNotExistException;
use System\Configure\Exception\InvalidFileExtException;
use System\Configure\Exception\UnsupportedParserException;

/**
 * 配置类
 *
 * @package Dream
 * @subpackage Config
 * @namespace System\Configure
 * @author lewis (lewisld@yeah.net)
 */
class Config extends ConfigAbstract
{

    /**
     * 所有支持的配置文件格式
     *
     * @access private
     * @var array
     */
    private $supportedFileParsers = array(
        'php' => 'System\Configure\Parser\Php',
        'ini' => 'System\Configure\Parser\Ini',
        'json' => 'System\Configure\Parser\Json',
        'xml' => 'System\Configure\Parser\Xml',
        'yaml' => 'System\Configure\Parser\Yaml',
        'yml' => 'System\Configure\Parser\Yaml'
    );

    /**
     * 构造函数
     *
     * @param string|array $path 配置文件路径
     */
    public function __construct($path = '')
    {
        if (! empty($path)) {
            return $this->load($path);
            parent::__construct($this->data);
        }
    }

    /**
     * 导入配置文件并加载
     *
     * @access public
     * @param string|array $path
     * @return Config
     */
    public function import($path)
    {
        return $this->load($path);
    }

    /**
     * 加载配置文件
     *
     * @access protected
     * @param string|array $path
     * @return void 没有返回值
     */
    protected function load($path)
    {
        if (true !== $this->fileList[$path]) {
            $paths = $this->getFilePath($path);
            $this->data = array();
            foreach ($paths as $path) {
                $info = pathinfo($path); // 获取文件信息
                $ext = isset($info['extension']) ? $info['extension'] : ''; // 获取配置文件扩展名
                if (empty($ext)) {
                    throw new InvalidFileExtException([
                        [
                            'system.conf.file.ext.invalid'
                        ],
                        [
                            'file' => $path
                        ]
                    ]);
                }
                $adapter = $this->getAdapter($ext);
                // 试图加载文件
                $this->data = array_replace_recursive($this->data, $adapter::parser($path));
                $this->fileList[$path] = true;
            }
            if (! empty($this->data['import'])) {
                $this->import($this->data['import']);
                $this->data['import'] = null;
            }
        }
    }

    /**
     * 获取配置文件解析器
     *
     * @access private
     * @param string $ext 解析器名称
     * @return AdapterInterface 返回配置文件解析器对象
     * @throws UnsupportedFormatException 不被支持的配置文件解析器异常
     */
    private function getAdapter($ext)
    {
        $adapter = null;
        $ext = strtolower($ext);
        if (isset($this->supportedFileParsers[$ext])) {
            $adapter = $this->supportedFileParsers[$ext];
        }
        if (null === $adapter) {
            throw new UnsupportedParserException([
                [
                    'system.conf.file.parser.unsupported'
                ],
                [
                    'parser' => $ext
                ]
            ]);
        }
        return $adapter;
    }

    /**
     * 获取配置文件地址
     *
     * @access public
     * @param string|array $path 配置文件地址，可以是字符串或数组
     * @return array 返回配置文件地址数组
     * @throws FileNotExistException 配置文件不存在异常
     * @throws FileNotReadException 配置文件不可读异常
     */
    private function getFilePath($path)
    {
        $paths = [];
        // If `$path` is array
        if (! is_array($path)) {
            $path = [
                $path
            ];
        }
        foreach ($path as $filePath) {
            if (is_array($filePath)) {
                array_merge($paths, $this->getFilePath($filePath));
            } else {
                array_merge($paths, [
                    PathAlias::getPath($filePath)
                ]);
            }
            if (! file_exists($filePath)) {
                throw new FileNotExistException([
                    [
                        'system.conf.file.not.exist'
                    ],
                    [
                        'file' => $filePath
                    ]
                ]);
            }
            if (! is_readable($filePath)) {
                throw new FileNotReadException([
                    [
                        'system.conf.file.not.read'
                    ],
                    [
                        'file' => $filePath
                    ]
                ]);
            }
        }
        return $paths;
    }
}